/**********************************************
*  Classes:   Loader3ds
*  Desc:      Loads .3ds files and creates
*		      MeshNodes
*  Notes:     Contains code by Damiano Vitulli
*  Author:    Vaios Kalpias-Ilias (C) 2008-2009
***********************************************/

#pragma warning( disable : 4996 )
#include "LoaderModel.h"
#include "GeometryNode.h"
#include "CdnRenderer.h"
#include "BoundingBox.h"
#include "CdnResourceLoader.h"
#include <fstream>
#include <stdio.h>
using namespace std;
using namespace mth;
//----------------------------------------------
/**
* Ctor
*/
LoaderModel::LoaderModel()
{
}
//----------------------------------------------
/**
* Dtor
*/
LoaderModel::~LoaderModel()
{
}
//----------------------------------------------
/**
* Loads a 3ds file and creates
* a meshnode to display it
*/
GeometryNode* LoaderModel::Load(const char *filename, CdnRenderer* renderer)
{
	// open file
	ifstream in (filename, ios::in | ios::binary);
	if (!in) return 0;
	
	// get polys and num of vertices

	int numPolys = 0;
	int numVerts = 0;

	in.read(reinterpret_cast<char*>(&numPolys), sizeof(int));
	in.read(reinterpret_cast<char*>(&numVerts), sizeof(int));

	// mesh must be triangulated to be rendered correctly
	if (numPolys > 0 && numVerts > 0)
	{
		if ((numVerts / numPolys)%3 != 0)
		{
			return 0;
		}
	}
	else
	{
		return 0;
	}

	
	// allocate memory for the file data
	float *v = new float[8*numVerts];
	if (!v) 
	{
		in.close();
		return 0;
	}

	// read file in one big batch
	in.read(reinterpret_cast<char*>(v), 8*numVerts*sizeof(float));
	in.close();

	
	// allocate memory for verts, normals, texCoords
	mth::Vector3* vertices = new mth::Vector3[numVerts];
	// check memory allocated
	if (!vertices) 
	{
		delete [] v;
		return 0;
	}

	mth::Vector3* normals = new mth::Vector3[numVerts];
	// check memory allocated
	if (!normals) 
	{
		delete [] vertices;
		delete [] v;
		return 0;
	}
	
	mth::Vector2* texCoords = new mth::Vector2[numVerts];
	// check memory allocated
	if (!texCoords) 
	{
		delete [] vertices;
		delete [] normals;
		delete [] v;
		return 0;
	}

	// get min max for bounding boxes
	vertices[0].x  = v[0];
	vertices[0].y  = v[1];
	vertices[0].z  = v[2];
	normals[0].x   = v[3];
	normals[0].y   = v[4];
	normals[0].z   = v[5];
	texCoords[0].x = v[6];
	texCoords[0].y = v[7];
	
	float minX = vertices[0].x;
	float minY = vertices[0].y;
	float minZ = vertices[0].z;
	float maxX = minX;
	float maxY = minY;
	float maxZ = minZ;


	// get rest of data
	for (int i=1; i<numVerts; i++)
	{
		int index = i*8;
		vertices[i].x  = v[index];
		vertices[i].y  = v[index + 1];
		vertices[i].z  = v[index + 2];
		normals[i].x   = v[index + 3];
		normals[i].y   = v[index + 4];
		normals[i].z   = v[index + 5];
		texCoords[i].x = v[index + 6];
		texCoords[i].y = v[index + 7];

		// calculate bounding box in the meantime
		if (minX > vertices[i].x) minX = vertices[i].x;
		if (minY > vertices[i].y) minY = vertices[i].y;
		if (minZ > vertices[i].z) minZ = vertices[i].z;
		if (maxX < vertices[i].x) maxX = vertices[i].x;
		if (maxY < vertices[i].y) maxY = vertices[i].y;
		if (maxX < vertices[i].z) maxZ = vertices[i].z;
	}

	delete [] v;

	// create mesh
	GeometryNode* mesh = new GeometryNode();
	// bind data to mesh
	bool res = renderer->BindNodeToMesh
		(mesh, vertices, normals, texCoords, numVerts);

	if (vertices)
		delete [] vertices;

	if (normals)
		delete [] normals;

	if (texCoords)
		delete [] texCoords;

	if (!res)
	{
		delete mesh;
		mesh = 0;
	}
	else
	{
		// set bounding box
		BoundingBox b(maxX, minX, maxY, minY, maxZ, minZ);
		mesh->SetInitialBoundingBox(b);
	}


	return mesh;	
}